<?php
/**
 * @see Zend_Form
 */
require_once 'Zend/Form.php';

/**
 * Wufoo_Form
 *
 * @category   Wufoo
 * @package    Wufoo_Form
 * @copyright  Copyright (c) 2009-2010 Eyeflow Internet Marketing. (http://www.eyeflow.com)
 * @author     Sergei Izvorean
 */
class Wufoo_Form extends Zend_Form
{
    /**
     * Wufoo username
     * Form option: wufooUsername
     *
     * @see Wufoo_Form::setWufooUsername()
     * @var string
     */
    private $username;

    /**
     * Api key
     * Form option: wufooApiKey
     * 
     * @see Wufoo_Form::setWufooApiKey()
     * @var string
     */
    private $apiKey;

    /**
     * Wufoo form name
     * Form option: wufooForm
     *
     * @see Wufoo_Form::setWufooForm()
     * @var string
     */
    private $formName;

    /**
     * Used to conver field/group titles into PHP/HTML-friendly names
     *
     * @see Wufoo::getNameFilter
     * @var Zend_Filter
     */
    protected static $nameFilter;

    /**
     * Cache the form structure
     * Form option: cache
     *
     * @see Wufoo_Form::setCache()
     * @see Wufoo_Form::getCache()
     * @var Zend_Cache_Core
     */
    protected static $cache;

    /**
     * List of Wufoo element types that are display groups (fieldset)
     *
     * @var array
     */
    protected static $_groupTypes = array('shortname', 'address');


    /**
     * Initialize form elements
     *
     * @return void
     */
    public function init()
    {
        // Check username
        if (strlen($this->username) == 0) {
            require_once 'Wufoo/Form/Exception.php';
            throw new Wufoo_Form_Exception('Must set Wufoo username');
        }

        // Check api key
        if (strlen($this->apiKey) == 0) {
            require_once 'Wufoo/Form/Exception.php';
            throw new Wufoo_Form_Exception('Must set Wufoo API key');
        }

        if (strlen($this->formName) == 0) {
            require_once 'Wufoo/Form/Exception.php';
            throw new Wufoo_Form_Exception('Form name must be set using Wufoo_Form::setWufooForm() or "wufooForm" config option.');
        }

        // Check cache before connecting to Wufoo API
        $cacheKey = md5($this->formName);

        if (!$json = self::getCache()->load($cacheKey)) {
            $client = new Zend_Http_Client();

            // Query for form information
            $client->setUri('http://'.$this->username.'.wufoo.com/api/query/')
                   ->setParameterPost(array(
                       'w_api_key' => $this->apiKey,
                       'w_version' => '2.0',
                       'w_form' => $this->formName,
                       'w_page_size' => 1, // we are interested in form structure
                   ));
            $response = $client->request(Zend_Http_Client::POST);
            if ($response->isError()) {
                require_once 'Wufoo/Form/Exception.php';
                throw new Wufoo_Form_Exception('Unable to query Wufoo API: '.$response->getMessage());
            }

            $json = json_decode($response->getBody());
            if (!$json->success) {
                require_once 'Wufoo/Exception.php';
                throw new Wufoo_Exception('Error while querying Wufoo API: '.$json->error);
            }

            // Save the properly parsed json response
            self::getCache()->save($json, $cacheKey);
        }

        /**
         * Convert each field to {@see Zend_Form_Element}
         */
        $fields = $json->form->Fields;

        /**
         * @var $field stdClass
         */
        foreach ($fields as $field) {
            if (!isset($field->FieldId)) continue;

            if (in_array($field->Typeof, self::$_groupTypes)) {
                // Create a display group of elements within
                //$displayGroup->setOrder($group->Pos);
                $group = $field;
                $groupFields = array();
                foreach ($group->SubFields as $pos => $field) {
                    $field = (object)array_merge((array)$group, (array)$field);
                    $field->Title = empty($field->ChoicesText) ? $field->Title : $field->ChoicesText;
                    $field->Pos = $pos;
                    $element = Wufoo_Form_Element::factory($field);
                    $this->addElement($element);

                    $groupFields[] = $element->getName();
                }

                $groupName = self::getNameFilter()->filter($group->Title);
                $this->addDisplayGroup($groupFields, $groupName, array('legend' => $group->Title));
            } else {
                // Basic html element
                $element = Wufoo_Form_Element::factory($field);

                $this->addElement($element);
            }
        }
    }

    /**
     * Submit form values to Wufoo service. This method does not validate the form, yet it does
     * add validation error messages if field errors are found
     *
     * @return boolean
     */
    public function submitWufoo()
    {
        $values = $this->getValues();
        $client = new Zend_Http_Client();
        $idElements = array(); // list to back reference to an element by its api id
        $params = array(
           'w_api_key' => $this->apiKey,
           'w_form' => $this->formName,
        );

        foreach ($values as $name => $value) {
            $element = $this->getElement($name);
            $id = $element->apiId;
            if (!is_numeric($id)) continue;

            $idElements[$id] = $element;

            if ($element instanceof Zend_Form_Element_Multi) {
                /* @var $element Zend_Form_Element_Multi */
                $options = $element->getMultiOptions();
                if (is_array($value)) {
                    // Multiple values: checkboxes
                    foreach ($value as $v)
                        $params[$v] = $options[$v];
                } else {
                    // Single value: radio, select
                    $params[$id] = $options[$v];
                }
            } elseif ($element instanceof Wufoo_Form_Element_Date) {
                $filter = new Wufoo_Filter_Date();
                $params[$id] = $filter->filter($value);
            } else {
                $params[$id] = $value;
            }
        }

        $response = $client->setUri('http://'.$this->username.'.wufoo.com/api/insert/')
                           ->setParameterPost($params)
                           ->request(Zend_Http_Client::POST);
        if ($response->isError()) {
            // TODO: parse json response for better error message
            require_once 'Wufoo/Form/Exception.php';
            throw new Wufoo_Form_Exception('Error while communicating with Wufoo API: '.$response->getMessage());
        }

        $json = json_decode($response->getBody());
        $submit = $json->wufoo_submit[0];
        if ($submit->success != 'true') {
            if (strlen($submit->error) > 0) {
                // General error
                $this->addError($submit->error);
            }

            // Fields errors
            if (!empty($submit->field_errors)) {
                foreach ($submit->field_errors as $fieldError) {
                    if (!preg_match('/(\d+)$/i', $fieldError->field_id, $matches)) continue;

                    $id = intval($matches[1]);
                    isset($idElements[$id]) ? $idElements[$id]->addError($fieldError->error_message) : null;
                }
            }

            return false;
        }

        return true;
    }

    /**
     * A filter to convert field/group titles to PHP/HTML friendly names
     *
     * @return Zend_Filter
     */
    public static function getNameFilter()
    {
        if (self::$nameFilter instanceof Zend_Filter)
            return self::$nameFilter;

        $filter = new Zend_Filter();
        $filter->addFilter(new Zend_Filter_Word_CamelCaseToDash())
               ->addFilter(new Zend_Filter_StringToLower())
               ->addFilter(new Zend_Filter_Alnum());
               
        return $filter;
    }

    /**
     * Set cache to handle form structures (across all wufoo forms)
     *
     * @param $cache Zend_Cache
     * @return Wufoo_Form
     */
    protected static function setCache(Zend_Cache $cache)
    {
        self::$cache = $cache;

        return $this;
    }

    /**
     * Initialize/get current cache
     *
     * @return Zend_Cache_Core
     */
    protected static function getCache()
    {
        if (!(self::$cache instanceof Zend_Cache)) {
            self::$cache = Zend_Cache::factory('Core', 'File',
                array('lifetime' => 7200, // cache lifetime 2 hours
                      'automatic_serialization' => true),
                array('cache_dir' => sys_get_temp_dir()));
        }

        return self::$cache;
    }

    /**
     * Set wufoo api key
     * Implementation according to Zend_Form::setOptions()
     *
     * @param string $key
     * @return Wufoo_Form
     */
    public function setWufooApiKey($key)
    {
        $this->apiKey = $key;

        return $this;
    }
    
    /**
     * Wufoo api key used to create the form
     *
     * @return string
     */
    public function getWufooApiKey()
    {
        return $this->apiKey;
    }

    /**
     * Wufoo username used to create the form
     *
     * @return string
     */
    public function getWufooUsername()
    {
        return $this->username;
    }

    /**
     * Set wufoo username
     * Implementation according to Zend_Form::setOptions()
     *
     * @param <type> $username
     * @return Wufoo_Form
     */
    public function setWufooUsername($username)
    {
        $this->username = $username;
        
        return $this;
    }

    /**
     * Wufoo form name
     *
     * @return string
     */
    public function getWufooForm()
    {
        return $this->formName;
    }

    /**
     * Set wufoo form name
     * Implementation according to Zend_Form::setOptions()
     *
     * @param string $wufooForm
     * @return Wufoo_Form
     */
    public function setWufooForm($wufooForm)
    {
        $this->formName = $wufooForm;

        return $this;
    }
}
